﻿using System;
using System.Data;
using System.Text;
using System.Collections.Generic;
using System.Windows.Forms;
using SdmxMl.Common;
using SdmxMl.Helper;
using SdmxMl.Panels;
using SdmxMl.Structure;
using SdmxMl.Manager;

namespace SdmxMl.UC
{
    /// <summary> Control used to edit a hierarchy of a hierarchicalCodelist </summary>
    public partial class UcHierarchyCl : UserControl
    {
        #region Internal members

        private TreeNode bindNode;
        private PanelAnnotationList pannelAnnot;
        private HierarchyType hierarchy;
        private UcMlText ucMlText = null;
        private UcMlText ucMlLevelText = null;
        private TreeNode rootNode;
        private LevelType level;
        private BindingManagerBase bm = null;
        private UcTextFormat ucTextFormat = null;
        private TreeNavigation treeNavigate;
        private CodeRefType cr = null;
        private bool _isFinal;
        DlgAddCodeRef aDlg = null;
        private PanelAnnotationList panelAnnotationCode = null;


        private bool inBinding;

        #endregion //Internal members

        #region Construction

        public UcHierarchyCl()
        {
            InitializeComponent();

            panelAnnotationCode = new PanelAnnotationList();
            this.groupBoxCodeInfo.Controls.Add(panelAnnotationCode.BasePanel);

            treeNavigate = new TreeNavigation();
            treeNavigate.TreeSdmx = treeViewH;
            treeNavigate.RecordNavButtons(toolStripButtonPrevSel, toolStripButtonNextSelection, toolStripButtonNavParent, toolStripComboBoxHistory);

            ucMlText = new UcMlText();
            customComboBoxName.DropDownControl = ucMlText;
            ucMlLevelText = new UcMlText();
            customComboBoxLevelName.DropDownControl = ucMlLevelText;
            ucTextFormat = new UcTextFormat();
            customComboBoxTextType.DropDownControl = ucTextFormat;

        }

        #endregion //Construction

        #region Binding

        public void BindFromNode(TreeNode node, PanelAnnotationList panelAnnotation, bool isFinal)
        {
            bindNode = node;
            _isFinal = isFinal;
            pannelAnnot = panelAnnotation;
            hierarchy = null;
            if (node != null)
                hierarchy = node.Tag as HierarchyType;
            BindToControls();
        }

        public bool UpdateNodeTag(TreeNode node)
        {
            if (aDlg != null)
                aDlg.Close();

            if (node != null)
                BindFromControls();

            if (node != null && hierarchy != null && node.Text != hierarchy.ToString())
                node.Text = hierarchy.ToString();


            return false; // true on error
        }

        private void BindToControls()
        {
            inBinding = true;
            treeViewH.Nodes.Clear();
            if (hierarchy != null)
            {
                Visible = true;

                // Info tab
                textBoxID.Text = hierarchy.Id;
                textBoxVersion.Text = hierarchy.Version;
                ucDatePickerFrom.Date = hierarchy.ValidFrom;
                ucDatePickerTo.Date = hierarchy.ValidTo;
                textBoxUrn.Text = hierarchy.Urn;
                textBoxUri.Text = hierarchy.Uri;
                customComboBoxName.Text = hierarchy.Name.ToString();
                ucMlText.ML = hierarchy.Name;
                comboMultiLinesDescr.Text = hierarchy.Description.ToString();
                comboMultiLinesDescr.ML = hierarchy.Description;
                UpdateComboLevel();
                // External Navigation URI Command
                buttonNavigateUri.Visible = string.IsNullOrEmpty(hierarchy.Uri) == false;

                // Hierarchy tab
                ConstructTreeHierarchy(cr);

                // Level Tab
                BindLevelRow(null);
                DataTable tb = hierarchy.LevelList.GetTableRepresentation();
                dataGridView.DataSource = tb;
                if (hierarchy.LevelList.Count > 0)
                    BindLevelRow(hierarchy.LevelList[0]);
                EnableGrigButtons(true);

                UpdateAnnotation();
                IsFinalEditControls(_isFinal);
            }
            else
                Visible = false;
            inBinding = false;
        }

        // Manage Edition possibility
        private void IsFinalEditControls(bool isFinal)
        {
            bool enabled = isFinal == false;

            // Info Tab
            textBoxID.ReadOnly = isFinal;
            textBoxVersion.ReadOnly = isFinal;
            ucDatePickerFrom.IsFinal = isFinal;
            ucDatePickerTo.IsFinal = isFinal;
            textBoxUrn.ReadOnly = isFinal;
            textBoxUri.ReadOnly = isFinal;
            customComboBoxName.IsFinal = isFinal;
            comboMultiLinesDescr.IsFinal = isFinal;
            if (ucTextFormat.TF != null)
                ucTextFormat.IsFinal = isFinal;

            // Levels
            numTextBoxLevelId.ReadOnly = isFinal;
            numTextBoxOrder.ReadOnly = isFinal;
            textBoxLevelUrn.ReadOnly = isFinal;
            customComboBoxLevelName.IsFinal = isFinal;
            comboMultiLinesLevelDesc.IsFinal = isFinal;

            // Hierarchy
            textBoxCodeId.ReadOnly = true; // ALWAYS
            textBoxCodelistRefAlias.ReadOnly = true; // ALWAYS
            ucDatePickerHierarchyFrom.IsFinal = isFinal;
            ucDatePickerHierarchyTo.IsFinal = isFinal;
            textBoxCodeVersion.ReadOnly = isFinal;
            textBoxCodeNodeAlias.ReadOnly = isFinal;
            comboBoxLevelChoice.Enabled = enabled;

            // ToolStrip Bar
            toolStripButtonDelCodeRef.Enabled = enabled;
        }

        /// <summary>Reflect hierarchy in the Treeview</summary>
        /// <param name="nodeTagToSelect">node to select in tree after its construction</param>
        private void ConstructTreeHierarchy(object nodeTagToSelect)
        {
            new CWaitCursor();

            treeViewH.BeginUpdate();
            rootNode = new TreeNode(hierarchy.ToString());
            rootNode.Tag = hierarchy;
            treeViewH.Nodes.Clear();
            hierarchy.CodeRefList.GetTreeHierarchyAsNodes(rootNode, hierarchy.CodelistRefList.GetRefDictionary());

            treeViewH.Nodes.Add(rootNode);
            //treeViewH.ExpandAll();
            rootNode.Expand();
            treeViewH.EndUpdate();

            // Suggested object to select does it exist in tree ?
            TreeNode targetNode = null;
            TreeHelper.LocateObject(treeViewH, nodeTagToSelect, ref targetNode);
            if (targetNode != null)
            {
                treeViewH.SelectedNode = targetNode;
                treeViewH.SelectedNode.EnsureVisible();
            }
            else
            {
                if (rootNode.Nodes.Count > 0)
                {
                    treeViewH.SelectedNode = rootNode.Nodes[0];
                    treeViewH.SelectedNode.EnsureVisible();
                }
            }
            treeNavigate.Reset();
        }


        private void tabControl_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Remove add code dialog if present
            if (aDlg != null)
                aDlg.Close();

            // Hierarchy root node text update if hierarchy id changed
            if (rootNode != null && hierarchy != null)
            {
                if (hierarchy.Id != textBoxID.Text)
                {
                    hierarchy.Id = textBoxID.Text;
                    rootNode.Text = hierarchy.ToString();
                }
            }

            UpdateAnnotation();
        }

        /// <summary>
        /// Select and display an annotation list according 
        /// to selected tab in detail view
        /// </summary>
        private void UpdateAnnotation()
        {
            if (tabControl.SelectedIndex == 0)
                pannelAnnot.BindFromNode(hierarchy, _isFinal);
            else if (tabControl.SelectedIndex == 2)
                pannelAnnot.BindFromNode(level, _isFinal);

            else // NO annotation in hierarchy Coderef for SDMX 2.0
            {
                if (cr != null)
                    pannelAnnot.BindFromNode(cr, _isFinal);
                else
                    pannelAnnot.BindFromNode(null, _isFinal);
            }
        }

        protected bool BindFromControls()
        {
            bool err = false;
            if (hierarchy != null)
            {
                // Info page
                hierarchy.Id = textBoxID.Text;
                hierarchy.Urn = textBoxUrn.Text;
                hierarchy.Uri = textBoxUri.Text;
                hierarchy.Version = textBoxVersion.Text;
                hierarchy.ValidFrom = ucDatePickerFrom.Date;
                hierarchy.ValidTo = ucDatePickerTo.Date;
                hierarchy.Name.UpdateForCurrentLanguage(customComboBoxName.Text);
                hierarchy.Description.UpdateForCurrentLanguage(comboMultiLinesDescr.Text);

                // Hierarchy Tab
                UpdateRefFromControls();

                // Recreate CodelistRef hierarchy from tree
                hierarchy.CodeRefList.Clear();
                UpdateRefList(treeViewH.Nodes[0], hierarchy.CodeRefList);

                // Level Tab
                UpdateRow();
            }
            return err;
        }

        // recursive helper
        private void UpdateRefList(TreeNode parentNode, CodeRefTypeList crtl)
        {
            foreach (TreeNode node in parentNode.Nodes)
            {
                CodeRefType crt = node.Tag as CodeRefType;
                crtl.Add(crt);
                crt.CodeRefList.Clear();
                UpdateRefList(node, crt.CodeRefList);

            }
        }

        /// <summary>Update CodeRefeence from detail group control</summary>
        private void UpdateRefFromControls()
        {
            TreeNode nd = treeViewH.SelectedNode;
            if (nd != null)
            {
                CodeRefType cr = nd.Tag as CodeRefType;
                if (cr != null)
                {
                    //  cr.CodeID;              Cannot be changed
                    //  cr.CodelistAliasRef     Cannot be changed

                    cr.ValidFrom = ucDatePickerHierarchyFrom.Date;
                    cr.ValidTo = ucDatePickerHierarchyTo.Date;
                    cr.Version = textBoxCodeVersion.Text;
                    cr.NodeAliasID = textBoxCodeNodeAlias.Text;
                    cr.LevelRef = comboBoxLevelChoice.Text;
                }
            }
        }

        #endregion //Binding

        #region TreeView Events

        private void treeViewH_BeforeSelect(object sender, TreeViewCancelEventArgs e)
        {
            UpdateRefFromControls();
        }

        /// <summary>After Node selection, Update detail panel informations </summary>
        /// <param name="sender"></param> <param name="e"></param>
        private void treeViewH_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (e.Node != null)
            {
                cr = e.Node.Tag as CodeRefType;
                if (cr != null)
                {
                    groupBoxCodeRef.Visible = true;
                    textBoxCodeId.Text = cr.CodeID;
                    textBoxCodelistRefAlias.Text = cr.CodelistAliasRef;
                    ucDatePickerHierarchyFrom.Date = cr.ValidFrom;
                    ucDatePickerHierarchyTo.Date = cr.ValidTo;
                    textBoxCodeVersion.Text = cr.Version;
                    textBoxCodeNodeAlias.Text = cr.NodeAliasID;
                    comboBoxLevelChoice.Text = cr.LevelRef;
                }
                else
                {
                    groupBoxCodeRef.Visible = false;
                }
                UpdateSelectedCodeInfo(cr);

            }

            DisplayInfo(TreeHelper.CalculateNodeChilds(treeViewH.SelectedNode));
        }

        private void UpdateSelectedCodeInfo(CodeRefType crt)
        {
            if (hierarchy != null && crt != null)
            {
                CodeType code = hierarchy.CodeRefList.GetCodeOfRef(crt,
                hierarchy.CodelistRefList.GetRefDictionary());
                panelAnnotationCode.BindFromNode(code, true);
                buttonNavigateCodelist.Visible = code != null;
            }
            else
            {
                buttonNavigateCodelist.Visible = false;
                panelAnnotationCode.BindFromNode(null, true);
            }

            UpdateAnnotation();
        }

        /// <summary>Navigation to referred codelist request</summary>
        /// <param name="sender"></param> <param name="e"></param>
        private void buttonNavigateCodelist_Click(object sender, EventArgs e)
        {
            //Fetch codelist & code
            if (hierarchy != null && cr != null)
            {
                try
                {
                    Dictionary<string, CodeListType> dico = hierarchy.CodelistRefList.GetRefDictionary();
                    CodeType code = null;
                    if (dico != null && dico.ContainsKey(cr.CodelistAliasRef))
                    {
                        CodeListType clType = dico[cr.CodelistAliasRef];
                        code = clType.CodeList.LocateItem(cr.CodeID) as CodeType;

                        // Construct urn
                        string urn = UrnManager.FullIdToUrn(
                        SdmxArtefactType.CodeLists.ToString(),
                            clType.FullIdent, code, null);

                        //navigate from it
                        UrnParser p = new UrnParser(urn);

                        // If already in memory, navigate otherwhile get from regidtry
                        if (NavigationManager.LocatenavigateProject(SdmxArtefactType.CodeLists,
                            clType.FullIdent, p) == null)
                            NavigationManager.NavigateWithRegistry(SdmxArtefactType.CodeLists,
                            clType.FullIdent, false, p);

                    }
                }
                catch
                {
                }
            }
        }


        // Info in status bar
        public void DisplayInfo(string msg)
        {
            if (msg != null)
                toolStripStatusLabel.Text = msg;
        }

        #endregion //TreeView Events

        #region Levels

        private void UpdateComboLevel()
        {
            comboBoxLevelChoice.Items.Clear();
            comboBoxLevelChoice.Items.Add(string.Empty);
            foreach (LevelType lv in hierarchy.LevelList)
                comboBoxLevelChoice.Items.Add(lv.Id);

        }

        public void BindLevelRow(LevelType rowLevel)
        {
            if (level != rowLevel)
            {

                level = rowLevel;
                if (level != null)
                {
                    pannelAnnot.BindFromNode(level, _isFinal);
                    numTextBoxLevelId.Text = level.Id;
                    numTextBoxOrder.Text = level.Order.ToString();
                    textBoxLevelUrn.Text = level.Urn;
                    customComboBoxLevelName.Text = level.Name.ToString();
                    ucMlLevelText.ML = level.Name;
                    comboMultiLinesLevelDesc.Text = level.Description.ToString();
                    comboMultiLinesLevelDesc.ML = level.Description;
                    customComboBoxTextType.Text = level.CodingType.ToString();
                    ucTextFormat.TF = level.CodingType;
                }
                else
                {
                    numTextBoxLevelId.Text = string.Empty;
                    numTextBoxOrder.Text = string.Empty;
                    textBoxLevelUrn.Text = string.Empty;
                    comboMultiLinesLevelDesc.Text = string.Empty;
                    comboMultiLinesLevelDesc.ML = null;
                    customComboBoxLevelName.Text = null;
                    ucMlLevelText.ML = null;
                    customComboBoxTextType.Text = null;
                    ucTextFormat.TF = null;
                    pannelAnnot.BindFromNode(null, _isFinal);

                }
                UpdateAnnotation();
            }
        }




        /// <summary>Update Grid buttons state</summary>
        /// <param name="enable">If true buttons are enabled</param>
        protected void EnableGrigButtons(bool enable)
        {
            butAdd.Enabled = enable;
            butRemove.Enabled = enable && hierarchy.LevelList != null && hierarchy.LevelList.Count > 0 && dataGridView.SelectedCells.Count > 0;

            if (_isFinal)
                butAdd.Enabled = butRemove.Enabled = false;
        }

        private void butAdd_Click(object sender, EventArgs e)
        {
            LevelType lv = new LevelType();
            lv.Id = "undefined";
            hierarchy.LevelList.Add(lv);
            lv.Order = hierarchy.LevelList.Count;
            dataGridView.DataSource = hierarchy.LevelList.GetTableRepresentation();
            dataGridView.FirstDisplayedScrollingRowIndex = hierarchy.LevelList.Count - 1;
            SelectGridRow(dataGridView, hierarchy.LevelList.Count - 1);
            UpdateComboLevel();
            dataGridView_SelectionChanged(this, EventArgs.Empty);
            EnableGrigButtons(true);
        }



        private void butRemove_Click(object sender, EventArgs e)
        {
            bm = dataGridView.BindingContext[dataGridView.DataSource, dataGridView.DataMember];
            if (bm.Count > 0)
            {
                DataRow findRow = ((DataRowView)bm.Current).Row;
                if (findRow != null)
                {
                    int index = (dataGridView.DataSource as DataTable).Rows.IndexOf(findRow);
                    hierarchy.LevelList.RemoveAt(index);
                    dataGridView.DataSource = hierarchy.LevelList.GetTableRepresentation();
                    index = Math.Min(hierarchy.LevelList.Count - 1, index);
                    SelectGridRow(dataGridView, index);
                    EnableGrigButtons(true);
                    UpdateComboLevel();
                }
                if (hierarchy.LevelList.Count == 0)
                    BindLevelRow(null);
                else
                    dataGridView_SelectionChanged(this, EventArgs.Empty);


            }

        }

        private void SelectGridRow(DataGridView dgv, int index)
        {
            if (index < 0)
                return;

            if (dgv.CurrentCell != null)
                dgv.CurrentCell.Selected = false;
            dgv.Rows[index].Cells[0].Selected = true;
            dgv.CurrentCell = dgv.SelectedCells[0];

        }

        private void dataGridView_SelectionChanged(object sender, EventArgs e)
        {
            if (inBinding)
                return;

            // dataGridView.so = SortOrder.None;
            UpdateRow();
            bm = dataGridView.BindingContext[dataGridView.DataSource, dataGridView.DataMember];
            if (bm.Count > 0)
            {
                DataRow findRow = ((DataRowView)bm.Current).Row;
                if (findRow != null)
                {
                    int index = (dataGridView.DataSource as DataTable).Rows.IndexOf(findRow);
                    LevelType rowLevel = null;
                    if (index >= 0 && index < hierarchy.LevelList.Count)
                    {
                        rowLevel = hierarchy.LevelList[index];
                        BindLevelRow(rowLevel);
                    }
                }
            }


        }
        public void UpdateRow()
        {
            if (level != null)
            {
                DataTable tb = dataGridView.DataSource as DataTable;
                dataGridView.EndEdit();
                if (tb != null && hierarchy != null)
                {
                    int rowIndex = hierarchy.LevelList.IndexOf(level);
                    if (rowIndex >= 0 && tb != null)
                    {
                        if (tb.Rows.Count <= rowIndex)
                        {
                        }
                        else
                        {
                            // Update row
                            level.Id = numTextBoxLevelId.Text;
                            level.Urn = textBoxLevelUrn.Text;
                            level.Order = Convert.ToInt32(numTextBoxOrder.Text);
                            level.CodingType = ucTextFormat.TF;

                            //  level.CodingType

                            tb.Rows[rowIndex].ItemArray = hierarchy.LevelList.GetRowObjects(hierarchy.LevelList[rowIndex]).ToArray();
                            tb.Rows[rowIndex].AcceptChanges();
                        }
                    }
                }
            }
        }

        private void customComboBoxTextType_DropDownClosed(object sender, EventArgs e)
        {
            if (level != null)
            {
                customComboBoxTextType.Text = level.CodingType.TextType.ToString();
                UpdateRow();
            }
        }

        #endregion Levels

        #region ToolStripHierarchy

        private void toolStripButtonNewCode_Click(object sender, EventArgs e)
        {
            if (aDlg == null)
            {
                aDlg = new DlgAddCodeRef(hierarchy, treeViewH, _isFinal);
                aDlg.FormClosed += new FormClosedEventHandler(aDlg_FormClosed);
                aDlg.Show();
            }
            else
                aDlg.ProposeLevel();
        }

        void aDlg_FormClosed(object sender, FormClosedEventArgs e)
        {
            aDlg = null;
        }

        private void toolStripButtonDelCodeRef_Click(object sender, EventArgs e)
        {
            if (treeViewH.SelectedNode != null && treeViewH.SelectedNode != rootNode)
            {
                if (MessageBox.Show("Delete selected item and its childs?", "Check",
                    MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    // Determines nodes to locate after deletion
                    TreeNode nodeToSelect = treeViewH.SelectedNode.Parent;
                    if (nodeToSelect.Nodes.IndexOf(treeViewH.SelectedNode) > 0)
                        nodeToSelect = nodeToSelect.Nodes[nodeToSelect.Nodes.IndexOf(treeViewH.SelectedNode) - 1];
                    else if (nodeToSelect.Nodes.Count > 1)
                        nodeToSelect = nodeToSelect.Nodes[nodeToSelect.Nodes.IndexOf(treeViewH.SelectedNode) + 1];

                    // Process delete
                    treeViewH.SelectedNode.Remove();
                }
            }
        }

        private void toolStripButtonExpandCollapse_Click(object sender, EventArgs e)
        {
            if (treeViewH.SelectedNode != null)
            {
                treeViewH_BeforeSelect(this, null);
                treeViewH.BeginUpdate();

                // if no child operate on parent if possible
                TreeNode n = treeViewH.SelectedNode;
                if (n.Nodes.Count == 0 && n.Parent != null)
                {
                    // Update as event locked
                    treeViewH_AfterSelect(this, new TreeViewEventArgs(n));
                    n = n.Parent;
                    treeViewH.SelectedNode = n;
                }

                if (n.IsExpanded)
                    n.Collapse();
                else
                    n.ExpandAll();
                n.EnsureVisible();
                treeViewH.EndUpdate();
            }

        }

        #endregion // ToolStripHierarchy

        #region HGDE Snapshot generation

        private void toolStripButtonGmf_Click(object sender, EventArgs e)
        {
            Artefact a = TreeHelper.GetParentArtefact(bindNode);

            SdmxMl.Panels.DlgHistorySnapshot aDlgH = new DlgHistorySnapshot(a as HierarchicalCodelistType);
            aDlgH.ShowDialog();
        }
        #endregion

        #region Navigation according to Urn

        private void buttonNavigateUri_Click(object sender, EventArgs e)
        {
            try
            {
                // If URN try to get it if not already in memory
                ArtefactRefTyped art = UrnManager.UrnToArtefactRefTyped(hierarchy.Uri);
                if (art != null)
                {
                    UrnParser p = new UrnParser(hierarchy.Uri);

                    // If already in memory, navigate otherwhile get from regidtry
                    if (NavigationManager.LocatenavigateProject(art.Type, art.FullIdent, p) == null)
                        NavigationManager.NavigateWithRegistry(art.Type, art.FullIdent, true, p);
                }
                else
                {
                    // Try to process document
                    System.Diagnostics.Process myProc;
                    myProc = System.Diagnostics.Process.Start(hierarchy.Uri);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void textBoxUri_TextChanged(object sender, EventArgs e)
        {
            // External Navigation Command
            hierarchy.Uri = textBoxUri.Text;
            buttonNavigateUri.Visible = textBoxUri.Text.Length > 0;
        }
        #endregion

        #region Tree Sorting

        private void toolStripButtonSort_Click(object sender, EventArgs e)
        {
            try
            {
                if (hierarchy != null)
                {
                    hierarchy.Sort(Control.ModifierKeys == Keys.Control);
                    ConstructTreeHierarchy(cr);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

        }

        #endregion

        #region Check referrenced codes exist in codelists

        private void toolStripButtonCheckCodes_Click(object sender, EventArgs e)
        {
            try
            {
                if (hierarchy != null)
                {
                    IList<string> missList = null;
                    using (new CWaitCursor())
                    {
                        HierarchyRefChecker checker = new HierarchyRefChecker();
                        missList = checker.Check(hierarchy);
                    }
                    if (missList.Count > 0)
                    {
                        StringBuilder sb = new StringBuilder();
                        foreach (string miss in missList)
                            sb.Append(miss).Append(Environment.NewLine);
                        MessageBox.Show(sb.ToString(), "Missing Codes detected", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    }
                    else
                        MessageBox.Show("No error detected", "Check codes reference", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
           }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        #endregion
    }
}
